Skip to content

shell: nushell integration scripts#4630

Open
sim590 wants to merge 102 commits intojunegunn:masterfrom
sim590:nushell-integration
Open

shell: nushell integration scripts#4630
sim590 wants to merge 102 commits intojunegunn:masterfrom
sim590:nushell-integration

Conversation

@sim590
Copy link
Copy Markdown

@sim590 sim590 commented Dec 9, 2025

  • Keybindings alt_c, ctrl_r and ctrl_t
  • Completion functions:
    • base implementation for ** completion
    • extra completion functionalities for programs such as:
      • pass
      • ssh, scp, sftp, telnet
      • kill
      • cd, pushd, rmdir

Closes: #4122

imsys and others added 30 commits May 4, 2025 04:58
Fixes some path issues. There are still some other things to fix
Pass~[1] is a command line password storage utility. Possible queries
are deduced from the file GPG hierarchy under ~/.password-store.

[1]: https://www.passwordstore.org/
Not sure why it doesn't work in this context, but if those are not
removed, the completion process doesn't substitute the selected value
to '**'.
fix typo  shell/key-bindinds.nu → shell/key-bindings.nu
Comment thread README.md Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class Nushell integration to fzf, including keybindings, **<TAB> fuzzy completion via Nushell’s external completer mechanism, installation/uninstallation support, and CI/test coverage to prevent regressions.

Changes:

  • Add fzf --nushell flag that prints Nushell integration scripts (keybindings + completion)
  • Add Nushell install/uninstall handling (autoload fzf.nu) and update docs
  • Add Nushell integration tests + Linux CI dependency install for nushell

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
main.go Embeds and prints Nushell integration scripts when --nushell is used
src/options.go Adds --nushell option parsing and Options.Nushell flag
shell/key-bindings.nu Implements CTRL-T / CTRL-R / ALT-C bindings for Nushell
shell/completion.nu Implements ** completion via Nushell external completer + command-specific completers
install Adds Nushell to supported shells and generates autoload fzf.nu
uninstall Removes Nushell autoload file during uninstall
README.md Documents Nushell integration setup and limitations
test/lib/common.rb Adds Nushell shell harness + tmux prep behavior
test/test_shell_integration.rb Adds Nushell-specific integration tests (bindings + completion smoke test)
.github/workflows/linux.yml Installs nushell in CI to run Nushell tests
typos.toml Adds Slq to allowed words (for pacman -Slq)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread shell/completion.nu
Comment on lines +374 to +375
# Assuming standard ps output where PID is the second column
$selected_line | lines | each { $in | from ssv --noheaders | get 0.column1 } | to text
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a false positive. from ssv --noheaders assigns generic column names: column0 = USER, column1 = PID, column2 = PPID, etc. So get 0.column1 correctly extracts the PID. The comment on the line above documents this.

Comment thread README.md Outdated
Comment thread README.md Outdated
sim590 added 2 commits April 22, 2026 10:49
Replace the confusing 'run this in your regular shell' comment
with a pure Nushell command using 'save -f'.
The note references fzf 0.48.0 which only introduced --bash, --zsh,
and --fish. The --nushell flag will be available in a later release.
Comment thread README.md
Use 'recent versions of fzf' instead of a specific version number
as suggested by junegunn. Also add mkdir before save to handle the
case where the autoload directory does not exist yet.
@sim590 sim590 force-pushed the nushell-integration branch 2 times, most recently from f154fa3 to adbd8e7 Compare April 23, 2026 07:04
Comment thread test/test_shell_integration.rb Outdated
Comment thread test/test_shell_integration.rb Outdated
Comment on lines +1178 to +1192
def test_alt_c_command
set_var('FZF_ALT_C_COMMAND', 'echo /tmp')

tmux.prepare
tmux.send_keys 'cd /', :Enter

tmux.prepare
tmux.send_keys :Escape, :c
tmux.until { |lines| assert_equal 1, lines.match_count }
tmux.send_keys :Enter

tmux.prepare
tmux.send_keys 'pwd', :Enter
tmux.until { |lines| assert_equal '/tmp', lines[-1] }
end
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. Except for :pwd and 'pwd' which should make no difference.

Please review these test cases if redefinitions are necessary.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same — removed the redundant redefinition.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please review the other tests as well to see if we can minimize code duplication.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — also removed test_ctrl_r (identical to TestShell). The remaining overrides now have comments explaining why they differ:

  • test_ctrl_t_unicode: uses ^echo (external command) since Nushell's builtin echo outputs structured data
  • test_file_completion: Nushell-specific, tests single-selection only (the external completer replaces the token rather than appending)
  • test_ctrl_r_abort: only tests with foo — single/double quotes cause issues in Nushell's line editor

Copy link
Copy Markdown
Owner

@junegunn junegunn Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Nushell-specific, tests single-selection only (the external completer replaces the token rather than appending)

Can you elaborate? We missed the multi-selection bug because of the skipping, so I'd like to keep the test if possible.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. The test now covers multi-selection (Tab+Tab with 2 files), single selection, and hidden files. The previous override only tested single selection, which is how the multi-selection bug slipped through. Fixed in 4ec77b9.

@junegunn
Copy link
Copy Markdown
Owner

junegunn commented Apr 23, 2026

Let me just randomly post issues I noticed in the comments.

~/github/fzf> pass **Error executing data_gen closure. Closure code: closure_635. Actual error: {msg: No matches found for Expand("~/.password-store/**/*.gpg"), debug: Generic(GenericError { code: "nu::shell::error", error: "No matches found for Expand(\"~/.password-store/**/*.gpg\")", msg: "Pattern, file or folder not found", site: Span(Span[165878..165904]), help: Some("no matches found"), inner: [], source: None }), raw: Generic(GenericError { code: "nu::shell::error", error: "No matches found for Expand(\"~/.password-store/**/*.gpg\")", msg: "Pattern, file or folder not found", site: Span(Span[165878..165904]), help: Some("no matches found"), inner: [], source: None }), rendered: Error: nu::shell::error

                                              × No matches found for Expand("~/.password-store/**/*.gpg")
                                                                                                              ╭─[/Users/***/Library/Application Support/nushell/autoload/fzf.nu:528:8]
                                                                              527 │   let passwordstore_files_gen_closure = {||
                528 │     ls ~/.password-store/**/*.gpg | get name | each {$in | str replace -r '^.*?\.password-store/(.*).gpg' '${1}' }
                             ·        ─────────────┬────────────
                                                                     ·                     ╰── Pattern, file or folder not found
                 529 │   }
                               ╰────
                                      help: no matches found
                                                            , json: {"msg":"No matches found for Expand(\"~/.password-store/**/*.gpg\")","labels":[{"text":"Pattern, file or folder not found","span":{"start":165878,"end":165904}}],"code":"nu::shell::error","url":null,"help":"no matches found","inner":[]}}

@junegunn
Copy link
Copy Markdown
Owner

Fuzzy completion does not respect --tmux option in my $FZF_DEFAULT_OPTS. CTRL-T does.

export FZF_DEFAULT_OPTS='--tmux 90%,70%'

Comment thread README.md Outdated
@junegunn
Copy link
Copy Markdown
Owner

Multi-selection (tab) in fuzzy completion doesn't work properly.

~/github/fzf> vim .editorconfig
::: .gitignore
::: .goreleaser.yml

It works for ^kill.

sim590 added 6 commits April 23, 2026 16:43
Wrap the ls glob in a try/catch so that pass completion
returns an empty list instead of a verbose error when
~/.password-store does not exist.
When multiple files are selected with Tab in fzf, join them
with spaces so the completer returns a single result instead
of separate candidates displayed with ::: separators.
Options from FZF_DEFAULT_OPTS are already parsed and passed as
CLI arguments. Without clearing the env var, fzf reads it again,
causing --height (CLI) to override --tmux (env) since CLI args
take precedence over FZF_DEFAULT_OPTS.
Rewrite __fzf_defaults_nu to return a string (like bash's
__fzf_defaults) instead of a list, and pass it via FZF_DEFAULT_OPTS
with-env. This ensures --height has a lower index than --tmux from
the user's FZF_DEFAULT_OPTS, so --tmux wins when both are present.

Also fix a critical bug where the non-existent is-string command
silently swallowed all options from FZF_DEFAULT_OPTS.
These tests were identical to the ones inherited from TestShell
via include, so the redefinitions were unnecessary.
@sim590
Copy link
Copy Markdown
Author

sim590 commented Apr 23, 2026

Thanks for testing! Here's a summary of the fixes:

  • pass crash: Fixed in 8057be90 — added try/catch so pass **<TAB> no longer errors when ~/.password-store doesn't exist.
  • Multi-selection: Fixed in e127c4ef — selected paths are now joined with lines | str join ' ' instead of being returned as separate entries.
  • --tmux in FZF_DEFAULT_OPTS: Fixed in 0025d228 — __fzf_defaults_nu now builds FZF_DEFAULT_OPTS as a string (like bash's __fzf_defaults), so --tmux takes precedence over --height. The root cause was twofold: (1) is-string doesn't exist in Nushell, so a filter was silently dropping all FZF_DEFAULT_OPTS entries, and (2) --height was passed as a CLI argument (higher index) instead of through FZF_DEFAULT_OPTS (where --tmux could override it).

Remove test_ctrl_r which was identical to the inherited TestShell
version. Add comments explaining why the remaining overrides are
necessary:
- test_ctrl_t_unicode: uses ^echo (external) instead of echo
- test_file_completion: single-selection only (completer replaces token)
- test_ctrl_r_abort: skips quote characters that break Nushell
Comment thread test/test_shell_integration.rb Outdated
@junegunn
Copy link
Copy Markdown
Owner

Coding agents can miss subtle issues that only surface in hands-on use, which matters a lot for an interactive tool like fzf. Could you share a list of what you manually tested, ideally with screenshots?

Nushell passes ~/path to the external completer with the tilde
unexpanded. Expand it with 'path expand' before passing it to fzf
as --walker-root, and restore the tilde prefix in the result.
@sim590 sim590 force-pushed the nushell-integration branch from 54c2615 to b4e1992 Compare April 24, 2026 02:31
sim590 added 2 commits April 23, 2026 23:01
…letion

Cover multi-selection (Tab+Tab), single selection, and hidden
files. Previously only single selection was tested, which let
the multi-selection bug slip through unnoticed.
@sim590
Copy link
Copy Markdown
Author

sim590 commented Apr 24, 2026

Coding agents can miss subtle issues that only surface in hands-on use, which matters a lot for an interactive tool like fzf.

Yes. I agree.

Could you share a list of what you manually tested, ideally with screenshots?

I tested:

  • all keybindings
  • vim **<TAB>
    • with no prefix.
    • with absolute path prefix
    • with relative path prefix
    • with ~
    • with $env.HOME/**, but that doesn't work. I think that this is a limitation of Nushell's completion dipatcher
  • ^cd **<TAB>
  • ^kill **<TAB>
  • pass **<TAB>
  • pacman -S **<TAB>
  • pacman -Q **<TAB>

All of these work as expected. Here are some screenshots...

image image image image image image

vim and ctrl+t:

image image

Comment thread README.md
Comment on lines +846 to +848
- Custom completion extensibility (e.g. `_fzf_complete_COMMAND` in bash/zsh)
is not available. Custom completions are defined via a `match` statement
in `completion.nu`.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to implement this scheme? A hard-coded match statement feels quite limiting. It could easily turn into a "kitchen sink", with users continually requesting more commands to be added.

For what it's worth, I'm on macOS and don't use tools like pacman or pass, so having them in the default list feels a bit off. I imagine this concern will only grow as more items get added over time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci CI/GitHub Actions docs Documentation go Go code install Install/uninstall scripts shell Shell scripts test Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] nushell integration

6 participants